#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

#include <jsoncpp/json/json.h>

#include "common.hpp"
#include "index.hpp"
#include "log.hpp"

extern Log server_log;

namespace qyc
{
    typedef struct doc_map_word
    {
        uint64_t _doc_id;
        size_t _weight;
        std::vector<std::string> _words;
    }doc_map_word_t;

    class search_html
    {
    public:
        void init_search(const std::string& path)
        {
            _index = doc_index::get_instance();
            // std::cout << "获取单例对象,建立索引" << std::endl;
            server_log(Info, "获取单例对象,建立索引");
            _index->build_index(path);
            // std::cout << "建立索引完成" << std::endl;
            server_log(Info, "建立索引完成");
        }
        //              query搜索语句       json_string返回json字符串
        void search(const std::string& query, std::string& json_string)
        {
            // 对用户输入的搜索语句进行分词
            std::vector<std::string> words;
            string_word::cut_string(query, words);

            // std::cout << "用户输入分词完成" << std::endl;
            server_log(Info, "用户分词完成");
            std::string log_print;
            for (const auto& e : words)
                log_print += "\\" + e;
            server_log(Info, "用户搜索分词结果: %s", log_print.c_str());

            // 根据分词进行查找
            // 存储去重后的结果
            std::vector<doc_map_word_t> invert_list_all;
            // 对分词搜索到的结果去重,合并权值
            std::unordered_map<uint64_t, doc_map_word_t> doc_map_word;
            for (std::string word : words)
            {
                boost::to_lower(word);
                typename doc_index::invert_list* plist = _index->get_invert_list(word);
                if (plist == nullptr)
                {
                    // std::cerr << word << " not found" << std::endl;
                    server_log(Info, "%s not found", word.c_str());
                    continue;
                }
                // 将该关键字搜索到的对应文档去重
                for (const invert_elem_t& elem : *plist)
                {
                    doc_map_word_t& it = doc_map_word[elem._doc_id];
                    it._doc_id = elem._doc_id;
                    it._weight += elem._weight;
                    it._words.push_back(elem._word);
                }
                // invert_list_all.insert(invert_list_all.end(), plist->begin(), plist->end());
            }
            for (const auto& item : doc_map_word)
                invert_list_all.push_back(std::move(item.second));
            // std::cout << "用户输入倒排拉链获取完成" << std::endl;
            server_log(Info, "用户倒排拉链完成");
            Json::Value root;
            if (invert_list_all.empty())
            {
                server_log(Info, "%s 没有与其对应的信息", query.c_str());
                Json::Value elem;
                elem["title"] = "query not found";
                elem["desc"] = query + " not found";
                elem["url"] = "#";
                root.append(elem);
            }
            else
            {
                // 根据权值进行降序排序
                // std::sort(invert_list_all.begin(), invert_list_all.end(), [](const invert_elem_t& left, const invert_elem_t& right) -> bool
                std::sort(invert_list_all.begin(), invert_list_all.end(), [](const doc_map_word_t& left, const doc_map_word_t& right) -> bool
                {
                    return left._weight > right._weight;
                });
                // 根据查找出的结果构建json串
                for (doc_map_word_t& item : invert_list_all)
                {
                    doc_info_index_t* doc = _index->get_forward_index(item._doc_id);
                    if (doc == nullptr)
                        continue;
                    Json::Value elem;
                    elem["title"] = doc->_html._title;
                    // 描述不用展示全部正文,将context的一部分截取出给desc
                    elem["desc"] = get_descibe(doc->_html._context, item._words[0]);
                    elem["url"] = doc->_html._url;
#ifdef __TEST__DEBUG__2
                    elem["weight"] = (int)item._weight;
#endif
                    root.append(elem);
                }
            }
            Json::StyledWriter write;
            json_string = write.write(root);
        }
    private:
        std::string get_descibe(const std::string& context, const std::string& word)
        {
            // 找到word在context中的首次出现,将该位置前50字节,后100字节返回
            static const size_t prev_step = 50;
            static const size_t next_step = 100;
            auto iter = std::search(context.begin(), context.end(), word.begin(), word.end(), [](char left, char right) -> bool
            {
                return std::tolower(left) == std::tolower(right);
            });
            if (iter == context.end())
                return "None";
            size_t pos = std::distance(context.begin(), iter);
            size_t begin = 0;
            if (pos >= prev_step)
                begin = pos - prev_step;
            size_t end = context.size();
            if (pos + next_step < end)
                end = pos + next_step;
            return context.substr(begin, end - begin) + "...";
        }
    private:
        doc_index* _index;
    };
}